package com.iocup.keybastion.core.client;


import com.iocup.keybastion.authentication.TokenService;
import com.iocup.keybastion.common.Cookie;
import com.iocup.keybastion.configuration.TokenExtractProperties;
import com.iocup.keybastion.context.WebContextHolder;
import com.iocup.keybastion.common.AuthConstant;
import com.iocup.keybastion.context.SecurityContextHolder;
import com.iocup.keybastion.core.credentials.TokenCredentials;
import com.iocup.keybastion.core.profile.UserProfile;
import com.iocup.keybastion.core.session.Session;
import com.iocup.keybastion.exception.AuthenticationException;
import com.iocup.keybastion.exception.CredentialsException;
import com.pine.sunflower.core.validate.Validator;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.Locale;
import java.util.Optional;

/**
 * @author xyjxust
 * @create 2022/3/4
 **/
public class LocalTokenBaseClient extends BaseClient<TokenCredentials> {
    @Setter
    private TokenExtractProperties tokenExtractProperties = new TokenExtractProperties();

    @Setter
    private TokenService tokenService;


    public void setTokenExtractProperties(TokenExtractProperties tokenExtractProperties) {
        Validator.build().validate(StringUtils.isEmpty(tokenExtractProperties.getHeaderName()), "headerName不能为空")
                .throwIfFail();
        this.tokenExtractProperties = tokenExtractProperties;
    }

    @Override
    public Optional<UserProfile> doGetUserDetail(TokenCredentials credentials, WebContextHolder contextHolder) {
        Session session = tokenService.getByToken(credentials.getToken());
        if (session == null) {
            return Optional.empty();
        }
        SecurityContextHolder.getContext().setToken(session.getToken());
        return Optional.of(session.getUserProfile());
    }

    @Override
    public Optional<UserProfile> refreshUserDetail(WebContextHolder contextHolder) {
        Optional<String> header = contextHolder.getRequestHeader(this.tokenExtractProperties.getRefreshTokenHeaderName());
        if (!header.isPresent()) {
            throw new AuthenticationException(String.format("未在header中发现%s", this.tokenExtractProperties.getRefreshTokenHeaderName()));
        }
        Session session = tokenService.createAndSaveByRefresh(header.get());
        if (session == null) {
            return Optional.empty();
        }
        SecurityContextHolder.getContext().setToken(session.getToken());
        return Optional.ofNullable(session.getUserProfile());
    }

    @Override
    public Optional<TokenCredentials> getCredentials(WebContextHolder webContextHolder) {
        Optional<String> optionalS = extractToken(webContextHolder);
        return optionalS.map(TokenCredentials::new);
    }

    @Override
    public void doLogoutAction(WebContextHolder webContextHolder, String loginName, String device, String targetUrl) {
        if (StringUtils.isBlank(loginName)) {
            loginName = SecurityContextHolder.getContext().getUserProfile().getLoginName();
        }
        if (StringUtils.isBlank(loginName)) {
            throw new AuthenticationException("未查找到登录用户名");
        }
        Session session = tokenService.getBySessionId(this.tokenService.extractKey(loginName, device));
        if (session != null) {
            this.tokenService.removeByToken(session.getToken().getAccessToken());
        }
    }

    @Override
    public String getType() {
        return AuthConstant.DEFAULT_CLIENT_NAME;
    }


    private Optional<String> extractToken(WebContextHolder contextHolder) {
        Optional<String> header = contextHolder.getRequestHeader(this.tokenExtractProperties.getHeaderName());
        if (!header.isPresent()) {
            header = contextHolder.getRequestHeader(this.tokenExtractProperties.getHeaderName().toLowerCase(Locale.ROOT));
        }
        if (header.isPresent()) {
            String headerWithoutPrefix = header.get();
            if (StringUtils.isNotEmpty(this.tokenExtractProperties.getPrefixHeader())) {
                String[] tokens = headerWithoutPrefix.split(" ");
                if (tokens.length != 2) {
                    throw new CredentialsException("凭证格式错误");
                }
                String authType = tokens[0];
                if (!this.tokenExtractProperties.getPrefixHeader().equalsIgnoreCase(authType)) {
                    throw new CredentialsException("凭证格式错误");
                }
                headerWithoutPrefix = tokens[1];
            }
            if (this.tokenExtractProperties.isHeaderTrimValue()) {
                headerWithoutPrefix = headerWithoutPrefix.trim();
            }
            return Optional.ofNullable(headerWithoutPrefix);
        }
        if (!header.isPresent() && !this.tokenExtractProperties.isCloseParameter()) {
            if (isGet(contextHolder) && !this.tokenExtractProperties.isSupportGetRequest()) {
                throw new CredentialsException("GET请求不支持");
            } else if (isPost(contextHolder) && !this.tokenExtractProperties.isSupportPostRequest()) {
                throw new CredentialsException("POST请求不支持");
            }
            header = contextHolder.getRequestHeader(this.tokenExtractProperties.getParameterName());
        }
        if (!header.isPresent() && !this.tokenExtractProperties.isCloseCookie()) {
            final Collection<Cookie> col = contextHolder.getRequestCookies();
            for (final Cookie c : col) {
                if (c.getName().equals(this.tokenExtractProperties.getCookieName())) {
                    header = Optional.ofNullable(c.getValue());
                }
            }
        }
        return header;


    }

    /**
     * Whether it is a GET request.
     *
     * @param context the web context
     * @return whether it is a GET request
     */
    public static boolean isGet(final WebContextHolder context) {
        return AuthConstant.HTTP_METHOD.GET.name().equalsIgnoreCase(context.getRequestMethod());
    }

    /**
     * Whether it is a POST request.
     *
     * @param context the web context
     * @return whether it is a POST request
     */
    public static boolean isPost(final WebContextHolder context) {
        return AuthConstant.HTTP_METHOD.POST.name().equalsIgnoreCase(context.getRequestMethod());
    }
}
